﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.ComponentModel.DataAnnotations;

namespace CharitiesLib
{
    public class UniqueAttribute : ValidationAttribute
    {
        public Func<ModelContainer> GetDataContext { get; private set; }
        public string IDProperty { get; private set; }
        public string Message { get; private set; }

        public UniqueAttribute(Type dataContextType, string idProperty, string message)
        {
            IDProperty = idProperty;
            Message = message;
            GetDataContext = () => (ModelContainer)Activator.CreateInstance(dataContextType);
        }

        public UniqueAttribute(Type dataContextType, string idProperty, string message, string connectionString)
        {
            IDProperty = idProperty;
            Message = message;
            GetDataContext = () => (ModelContainer)Activator.CreateInstance(dataContextType, new object[] { connectionString });
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var idProperty = validationContext.ObjectType.GetProperty(IDProperty);
            var idType = idProperty.PropertyType;
            var id = idProperty.GetValue(validationContext.ObjectInstance, null);

            // Unsightly hack due to validationContext.MemberName being null :(
            var memberName = validationContext.ObjectType.GetProperties()
                .Where(p => p.GetCustomAttributes(false).OfType<DisplayAttribute>().Any(a => a.Name == validationContext.DisplayName))
                .Select(p => p.Name)
                .FirstOrDefault();
            if (string.IsNullOrEmpty(memberName))
            {
                memberName = validationContext.DisplayName;
            }
            // End of hack

            var validateeProperty = validationContext.ObjectType.GetProperty(memberName);
            var validateeType = validateeProperty.PropertyType;
            var validatee = validateeProperty.GetValue(validationContext.ObjectInstance, null);

            var idParameter = Expression.Constant(id, idType);
            var validateeParameter = Expression.Constant(validatee, validateeType);
            var objectParameter = Expression.Parameter(validationContext.ObjectType, "o");
            var objectIDProperty = Expression.Property(objectParameter, idProperty);
            var objectValidateeProperty = Expression.Property(objectParameter, validateeProperty);
            var idCheck = Expression.NotEqual(objectIDProperty, idParameter);
            var validateeCheck = Expression.Equal(objectValidateeProperty, validateeParameter);
            var compositeCheck = Expression.And(idCheck, validateeCheck);
            var lambda = Expression.Lambda(compositeCheck, objectParameter);
            var countMethod = typeof(Queryable).GetMethods().Single(m => m.Name == "Count" && m.GetParameters().Length == 2);
            var genericCountMethod = countMethod.MakeGenericMethod(validationContext.ObjectType);

            //using (var context = GetDataContext())
            //{
            //    var table = context.GetTable(validationContext.ObjectType) as IQueryable;
            //    var count = (int)genericCountMethod.Invoke(null, new object[] { table, lambda });
            //    if (count > 0)
            //    {
            //        return new ValidationResult(Message);
            //    }
            //}

            return null;
        }
    }
}
